package org.yl.db_core.utils;

import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

/**
 * 存放静态字符串
 */
public final class GSONUtil {
    public String toJson() {
        Gson gson = getGson();
        String json = gson.toJson(this);

        return json;
    }

    public static final Gson getGson() {
        return getGson("yyyy-MM-dd HH:mm:ss.SSS");
    }

    private static final Gson getGson(String dateFmt) {
        GsonBuilder builder = new GsonBuilder();

        // builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
        //
        // public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // System.out.println(type.getType());
        // return new BaseBeanAdapter<T>();
        // }
        // });
        builder.registerTypeAdapter(Date.class, new DateAdapter());
        builder.registerTypeAdapter(Map.class, new MapAdapter());
        builder.setDateFormat(dateFmt);

        Gson gson = builder.create();
        return gson;
    }

    public static final Gson getGsonNoMapAdapter() {
        GsonBuilder builder = new GsonBuilder();

        builder.registerTypeAdapter(Date.class, new DateAdapter());
        builder.setDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

        Gson gson = builder.create();
        return gson;
    }

    public static final Gson getGson(final Class<?>... skipClass) {
        GsonBuilder builder = new GsonBuilder();
        if (skipClass != null && skipClass.length > 0) {
            ExclusionStrategy es = new ExclusionStrategy() {
                public boolean shouldSkipField(FieldAttributes f) {
                    return false;
                }

                public boolean shouldSkipClass(Class<?> cls) {
                    for (Class<?> skip : skipClass) {
                        if (cls.equals(skip)) {
                            return true;
                        }
                    }
                    return false;
                }
            };
            builder.setExclusionStrategies(es);
        }
        builder.registerTypeAdapter(Date.class, new DateAdapter());
        builder.registerTypeAdapter(Map.class, new MapAdapter());
        builder.setDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Gson gson = builder.create();
        return gson;
    }

    private static class DateFormat {
        private SimpleDateFormat sf1 = null;// yyyy-MM-dd;
        private SimpleDateFormat sf2 = null;// yyyy-MM-dd HH:mm:ss;
        private SimpleDateFormat sf3 = null;// yyyy-MM-dd HH:mm:ss.SSS;

        protected final SimpleDateFormat getSF(String val) {
            if (val.length() == 23) {
                if (sf3 == null) {
                    sf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                }
                return sf3;
            } else if (val.length() == 19) {
                if (sf2 == null) {
                    sf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                }
                return sf2;
            } else if (val.length() == 10) {
                if (sf1 == null) {
                    sf1 = new SimpleDateFormat("yyyy-MM-dd");
                }
                return sf1;
            }
            return null;
        }
    }

    // private static class BaseBeanAdapter<T> extends TypeAdapter<BaseBean> {
    // public JsonElement serialize(BaseBean bean, Type type, JsonSerializationContext context) {
    // return new JsonPrimitive(bean.toJSON().toString());
    // }
    // public void write(JsonWriter out, BaseBean value) throws IOException {
    // out.
    //
    // }
    // public BaseBean read(JsonReader in) throws IOException {
    // return null;
    // }
    // }

    private static class DateAdapter extends DateFormat implements JsonSerializer<Date>, JsonDeserializer<Date> {
        public Date deserialize(JsonElement ele, Type type, JsonDeserializationContext context) throws JsonParseException {
            if (ele == null) {
                return null;
            }
            String json = ele.getAsString();
            json = json.trim();
            SimpleDateFormat sf = getSF(json);
            if (sf == null) {
                return null;
            } else {
                try {
                    return sf.parse(json);
                } catch (ParseException e) {
                }
            }
            return null;
        }

        public JsonElement serialize(Date date, Type type, JsonSerializationContext context) {
            SimpleDateFormat sf = getSF("yyyy-MM-dd HH:mm:ss.SSS");
            return new JsonPrimitive(sf.format(date));
        }
    }

    private static class MapAdapter extends DateFormat implements JsonDeserializer<TreeMap<String, Object>> {

        public TreeMap<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            TreeMap<String, Object> treeMap = new TreeMap<>();
            JsonObject jsonObject = json.getAsJsonObject();

            for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
                Object ot = entry.getValue();
                if (ot == null) {
                    continue;
                }
                if (ot instanceof JsonPrimitive) {
                    JsonPrimitive jp = (JsonPrimitive) ot;
                    if (jp.isNumber()) {
                        Number num = jp.getAsNumber();
                        long longVal = num.longValue();
                        double doubleVal = num.doubleValue();
                        if (longVal == doubleVal) {
                            if (longVal == ((int) longVal)) {
                                treeMap.put(entry.getKey(), (int) longVal);
                            } else if (longVal == ((byte) longVal)) {
                                treeMap.put(entry.getKey(), (byte) longVal);
                            } else {
                                treeMap.put(entry.getKey(), longVal);
                            }
                        } else if (doubleVal == ((float) doubleVal)) {
                            treeMap.put(entry.getKey(), (float) doubleVal);
                        } else {
                            treeMap.put(entry.getKey(), num);
                        }
                    } else {
                        String str = jp.getAsString();
                        if (str.length() == 23) {// 疑似日期
                            try {
                                Date date = getSF(str).parse(str);
                                treeMap.put(entry.getKey(), date);
                                continue;
                            } catch (Exception e) {
                            }
                        }
                        treeMap.put(entry.getKey(), str);
                    }
                    continue;
                }
                if (ot instanceof JsonElement) {
                    treeMap.put(entry.getKey(), deserialize((JsonElement) ot, typeOfT, context));
                } else {
                    treeMap.put(entry.getKey(), ot);
                }
            }
            return treeMap;
        }

    }

    // private static class NumberAdapter extends DateFormat implements JsonDeserializer<Number> {
    //
    // public Number deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    // if (json == null) {
    // return null;
    // }
    // if (json instanceof JsonPrimitive) {
    // JsonPrimitive jp = (JsonPrimitive) json;
    // if (jp.isNumber()) {
    // Number num = jp.getAsNumber();
    // long longVal = num.longValue();
    // double doubleVal = num.doubleValue();
    // if (longVal == doubleVal) {
    // if (longVal == ((int) longVal)) {
    // return (int) longVal;
    // } else if (longVal == ((byte) longVal)) {
    // return (byte) longVal;
    // } else {
    // return longVal;
    // }
    // } else if (doubleVal == ((float) doubleVal)) {
    // return (float) doubleVal;
    // } else {
    // return num;
    // }
    // }
    // }
    // return null;
    // }
    //
    // }

}